import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class TinyGA {
	public TinyGA() {
		
	}
	// ------------------------------------------------------------------------------------------------------
	public interface Programa {
		public Error err();

		public List<Instruccion> cod();
	}
	// ------------------------------------------------------------------------------------------------------
	public interface Decs {
		public Error err();

		public int dir();

		public TS ts();
	}
	// ------------------------------------------------------------------------------------------------------
	public interface Dec {

		public CatLexica tipo();

		public String iden();

		public int fila();

		public int col();
	}
	// ------------------------------------------------------------------------------------------------------
	public interface Tipo {
		public CatLexica tipo();

	}
	// ------------------------------------------------------------------------------------------------------
	abstract public class Insts {

		private InstsCtx ctx;
		private TS tsh;
		private int etqh;
		private boolean hay_tsh;
		private boolean hay_etqh;

		protected Insts() {
			hay_tsh = false;
			hay_etqh = false;
		}

		abstract public Error err();

		abstract public List<Instruccion> cod();

		abstract public int etq();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public int etqh() {
			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public void registraCtx(InstsCtx ctx) {
			this.ctx = ctx;
		}

		public InstsCtx contexto() {
			return ctx;
		}
	}

	public interface InstsCtx {
		public TS tsh();

		public int etqh();

	}
	// ------------------------------------------------------------------------------------------------------
	abstract public class Inst {

		private InstCtx ctx;
		private TS tsh;
		private int etqh;
		private boolean hay_tsh;
		private boolean hay_etqh;

		protected Inst() {
			hay_tsh = false;
			hay_etqh = false;
		}

		abstract public int etq();

		abstract public Error err();

		abstract public List<Instruccion> cod();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public int etqh() {
			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public void registraCtx(InstCtx ctx) {
			this.ctx = ctx;
		}

		public InstCtx contexto() {
			return ctx;
		}
	}

	public interface InstCtx {
		public TS tsh();

		public int etqh();
	}
	// ------------------------------------------------------------------------------------------------------
	abstract public class Casos {

		private CasosCtx ctx;
		private TS tsh;
		private int etqh;
		private int irh;
		private boolean hay_tsh;
		private boolean hay_etqh;
		private boolean hay_irh;

		protected Casos() {
			hay_tsh = false;
			hay_etqh = false;
		}

		abstract public int etq();

		abstract public Error err();

		abstract public List<Instruccion> cod();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public int etqh() {
			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public int irh() {
			if (!hay_irh) {
				irh = ctx.irh();
				hay_irh = true;
			}
			return irh;
		}

		public void registraCtx(CasosCtx ctx) {
			this.ctx = ctx;
		}

		public CasosCtx contexto() {
			return ctx;
		}
	}

	public interface CasosCtx {

		public int etqh();

		public TS tsh();

		public int irh();
	}
	// ------------------------------------------------------------------------------------------------------
	abstract public class Caso {

		private CasoCtx ctx;
		private TS tsh;
		private int etqh;
		private int irh;
		private boolean hay_tsh;
		private boolean hay_etqh;
		private boolean hay_irh;

		protected Caso() {
			hay_tsh = false;
			hay_etqh = false;
			hay_irh = false;
		}

		abstract public int etq();

		abstract public Error err();

		abstract public List<Instruccion> cod();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public int etqh() {

			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public int irh() {
			if (!hay_irh) {
				irh = ctx.irh();
				hay_irh = true;
			}
			return irh;
		}

		public void registraCtx(CasoCtx ctx) {
			this.ctx = ctx;
		}

		public CasoCtx contexto() {
			return ctx;
		}
	}

	public interface CasoCtx {

		public int etqh();

		public TS tsh();

		public int irh();
	}
	// ------------------------------------------------------------------------------------------------------
	abstract public class Exp0 {

		private Exp0Ctx ctx;
		private TS tsh;
		private int etqh;

		private boolean hay_etqh;
		private boolean hay_tsh;

		protected Exp0() {
			hay_tsh = false;
			hay_etqh = false;
		}

		abstract public List<Instruccion> cod();

		abstract public int etq();

		abstract public CatLexica tipo();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public void registraCtx(Exp0Ctx ctx) {
			this.ctx = ctx;
		}

		public int etqh() {
			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public Exp0Ctx contexto() {
			return ctx;
		}

	}

	public interface Exp0Ctx {
		public TS tsh();

		public int etqh();
	}
	// ------------------------------------------------------------------------------------------------------
	abstract public class Exp1 {

		private Exp1Ctx ctx;
		private TS tsh;
		private boolean hay_tsh;
		private boolean hay_etqh;
		private int etqh;

		protected Exp1() {
			hay_tsh = false;
			hay_etqh = false;
		}

		abstract public List<Instruccion> cod();

		abstract public int etq();

		abstract public CatLexica tipo();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public int etqh() {
			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public void registraCtx(Exp1Ctx ctx) {
			this.ctx = ctx;
		}

		public Exp1Ctx contexto() {
			return ctx;
		}

	}

	public interface Exp1Ctx {
		public TS tsh();

		public int etqh();
	}

	// ------------------------------------------------------------------------------------------------------
	abstract public class Exp2 {

		private Exp2Ctx ctx;
		private TS tsh;
		private boolean hay_tsh;
		private boolean hay_etqh;
		private int etqh;

		protected Exp2() {
			hay_tsh = false;
			hay_etqh = false;
		}

		abstract public List<Instruccion> cod();

		abstract public int etq();

		abstract public CatLexica tipo();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public int etqh() {
			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public void registraCtx(Exp2Ctx ctx) {
			this.ctx = ctx;
		}

		public Exp2Ctx contexto() {
			return ctx;
		}

	}

	public interface Exp2Ctx {
		public TS tsh();

		public int etqh();
	}
	// ------------------------------------------------------------------------------------------------------
	abstract public class Exp3 {

		private Exp3Ctx ctx;
		private TS tsh;
		private boolean hay_tsh;
		private boolean hay_etqh;
		private int etqh;

		protected Exp3() {
			hay_tsh = false;
			hay_etqh = false;
		}

		abstract public List<Instruccion> cod();

		abstract public int etq();

		abstract public CatLexica tipo();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public int etqh() {
			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public void registraCtx(Exp3Ctx ctx) {
			this.ctx = ctx;
		}

		public Exp3Ctx contexto() {
			return ctx;
		}

	}

	public interface Exp3Ctx {
		public TS tsh();

		public int etqh();
	}
	// ------------------------------------------------------------------------------------------------------
	abstract public class Exp4 {

		private Exp4Ctx ctx;
		private TS tsh;
		private boolean hay_tsh;
		private boolean hay_etqh;
		private int etqh;

		protected Exp4() {
			hay_tsh = false;
			hay_etqh = false;
		}

		abstract public List<Instruccion> cod();

		abstract public int etq();

		abstract public CatLexica tipo();

		public TS tsh() {
			if (!hay_tsh) {
				tsh = ctx.tsh();
				hay_tsh = true;
			}
			return tsh;
		}

		public int etqh() {
			if (!hay_etqh) {
				etqh = ctx.etqh();
				hay_etqh = true;
			}
			return etqh;
		}

		public void registraCtx(Exp4Ctx ctx) {
			this.ctx = ctx;
		}

		public Exp4Ctx contexto() {
			return ctx;
		}

	}

	public interface Exp4Ctx {
		public TS tsh();

		public int etqh();
	}

	// ----------------------------------------------------------------------------------------------
	public interface OpComparacion {
		public CatLexica op();

		public List<Instruccion> cod();

	}
	// -----------------------------------------------------------------------------------------
	public interface OpAditivo {
		public CatLexica op();

		public List<Instruccion> cod();

	}
	// -----------------------------------------------------------------------------------------
	public interface OpMultiplicativo {
		public CatLexica op();

		public List<Instruccion> cod();

	}
	// -----------------------------------------------------------------------------------------
	public interface OpUnario {
		public CatLexica op();

		public List<Instruccion> cod();

	}
	
	// ------------------------------------------------------------------------------------------------------
	// ---------------------------------------ProgramaR1---------------------------------------------------
	
	public class ProgramaR1 implements Programa {
		private Decs decs;
		private Insts insts;
		private List<Instruccion> cod;
		private boolean hay_cod;
		private Error err;
		private boolean hay_err;

		public ProgramaR1(Decs decs, Insts insts) {
			this.decs = decs;
			this.insts = insts;
			this.hay_err = false;
			this.hay_cod = false;

			insts.registraCtx(new InstsCtx() {
				public TS tsh() {
					return ProgramaR1.this.decs.ts();
				}
				public int etqh() {
					return 0;
				}
			});
		}

		public Error err() {
			if (!hay_err) {
				err = joinErrors(decs.err(), insts.err());
				hay_err = true;
			}
			return err;
		}

		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = insts.cod();
				hay_cod = true;
			}
			return cod;
		}

	}
	// -------------------------------------------ProgramaR2--------------------------------------------------
	public class ProgramaR2 implements Programa {
		private Insts insts;
		private List<Instruccion> cod;
		private boolean hay_cod;
		private Error err;
		private boolean hay_err;

		public ProgramaR2(Insts insts) {

			this.insts = insts;
			this.hay_err = false;
			this.hay_cod = false;

			insts.registraCtx(new InstsCtx() {
				public TS tsh() {
					return creaTS();
				}

				public int etqh() {
					return 0;
				}
			});
		}

		public Error err() {
			if (!hay_err) {
				err = insts.err();
				hay_err = true;
			}
			return err;
		}

		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = insts.cod();
				hay_cod = true;
			}
			return cod;
		}

	}
	// --------------------------------------------DecsR1-------------------------------------------------
	public class DecsR1 implements Decs {

		private Dec dec;
		private TS ts;

		private Decs decs_1;
		private Error err;
		private boolean hay_err;
		private boolean hay_ts;

		public DecsR1(Decs decs_1, Dec dec) {
			this.dec = dec;
			this.decs_1 = decs_1;
			this.hay_err = false;
		}

		public TS ts() {
			if (!hay_ts) {
				ts = aniadeSimb(decs_1.ts(), dec.iden(), dec.tipo(),
						decs_1.dir());
				hay_ts = true;
			}
			return ts;
		}

		public Error err() {
			if (!hay_err) {
				err = joinErrors(
						decs_1.err(),
						errorSi(existeSimb(decs_1.ts(), dec.iden()),
								dec.fila(), dec.col(), "IDEN REPETIDO"));
				hay_err = true;
			}
			return err;
		}

		public int dir() {
			return decs_1.dir() + 1;
		}

	}
	// -----------------------------------------DECSR2------------------------------------------------------
	public class DecsR2 implements Decs {
		private Dec dec;
		private TS ts;

		private Error err;
		private boolean hay_err;
		private boolean hay_ts;

		public DecsR2(Dec dec) {
			this.dec = dec;
			this.hay_err = false;
		}

		public TS ts() {
			if (!hay_ts) {
				ts = aniadeSimb(creaTS(), dec.iden(), dec.tipo(), 0);
				hay_ts = true;
			}
			return ts;
		}

		public Error err() {
			if (!hay_err) {
				err = noError();
				hay_err = true;
			}
			return err;
		}

		public int dir() {
			return 1;
		}
	}
	// ----------------------------------------DECR1------------------------------------------------------
	public class DecR1 implements Dec {

		private Token iden;
		private Tipo tipo;

		private boolean hay_fila;
		private boolean hay_col;
		private int fila;
		private int col;

		public DecR1(TinyGA.Tipo tipo, Token IDEN) {
			this.tipo = tipo;
			this.iden = IDEN;

			this.hay_fila = false;
			this.hay_col = false;
		}

		public String iden() {

			return iden.leeLexema();
		}

		public CatLexica tipo() {

			return tipo.tipo();
		}

		public int fila() {
			if (!hay_fila) {
				fila = iden.leeFila();
				hay_fila = true;
			}
			return fila;

		}

		public int col() {
			if (!hay_col) {
				col = iden.leeCol();
				hay_col = true;
			}
			return col;
		}
	}
	// -------------------------------------------TipoR1----------------------------------------------------
	public class TipoR1 implements Tipo {
		private CatLexica tipo;

		public TipoR1(Token tDeInt) {
			this.tipo = tDeInt.leeCategoria();
		}

		public CatLexica tipo() {
			return tipo;
		}
	}
	// --------------------------------------------TipoR2--------------------------------------------------
	public class TipoR2 implements Tipo {
		private CatLexica tipo;

		public TipoR2(Token tDeBol) {
			this.tipo = tDeBol.leeCategoria();
		}

		public CatLexica tipo() {
			return tipo;
		}
	}
	// ---------------------------------------------INSTR1---------------------------------------------------
	public class InstsR1 extends Insts {
		private Insts insts_1;
		private Inst inst;
		private List<Instruccion> cod;
		private boolean hay_cod;
		private Error err;
		private boolean hay_err;

		public InstsR1(Insts insts_1, Inst inst) {
			this.insts_1 = insts_1;
			this.inst = inst;
			this.hay_err = false;

			insts_1.registraCtx(new InstsCtx() {
				public TS tsh() {
					return InstsR1.this.tsh();
				}

				public int etqh() {
					return InstsR1.this.etqh();
				}
			});

			inst.registraCtx(new InstCtx() {
				public TS tsh() {
					return InstsR1.this.tsh();
				}

				public int etqh() {
					return InstsR1.this.insts_1.etq();
				}
			});

		}

		public Error err() {
			if (!hay_err) {
				err = joinErrors(insts_1.err(), inst.err());
				hay_err = true;
			}
			return err;
		}

		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = concat(insts_1.cod(), inst.cod());
				hay_cod = true;
			}
			return cod;
		}

		public int etq() {
			return inst.etq();
		}

	}
	// ---------------------------------------------INSTR2---------------------------------------------------
	public class InstsR2 extends Insts {
		private Inst inst;
		private Error err;
		private boolean hay_err;
		private boolean hay_cod;
		private List<Instruccion> cod;

		public InstsR2(Inst inst) {
			this.inst = inst;
			this.hay_err = false;
			inst.registraCtx(new InstCtx() {
				public TS tsh() {
					return InstsR2.this.tsh();
				}

				public int etqh() {
					return InstsR2.this.etqh();
				}
			});

		}

		public Error err() {
			if (!hay_err) {
				err = inst.err();
				hay_err = true;
			}
			return err;
		}

		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = inst.cod();
				hay_cod = true;
			}
			return cod;
		}

		public int etq() {
			return inst.etq();
		}

	}
	// -----------------------------------------------InstR1------------------------------------------------
	public class InstR1 extends Inst {
		private Error err;
		private boolean hay_err;
		private boolean hay_cod;
		private List<Instruccion> cod;
		private Inst iasig;

		public InstR1(Inst iasig) {
			this.iasig = iasig;
			iasig.registraCtx(new InstCtx() {
				public TS tsh() {
					return InstR1.this.tsh();
				}

				public int etqh() {
					return InstR1.this.etqh();
				}
			});
		}

		@Override
		public int etq() {
			return iasig.etq();
		}

		@Override
		public Error err() {
			if (!hay_err) {
				err = iasig.err();
				hay_err = true;
			}
			return err;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = iasig.cod();
				hay_cod = true;
			}
			return cod;
		}
	}
	// --------------------------------------------------InstR2---------------------------------------------
	public class InstR2 extends Inst {
		private Error err;
		private boolean hay_err;
		private boolean hay_cod;
		private List<Instruccion> cod;
		private Inst iIF;

		public InstR2(Inst iIF) {
			this.iIF = iIF;
			iIF.registraCtx(new InstCtx() {
				public TS tsh() {
					return InstR2.this.tsh();
				}

				public int etqh() {
					return InstR2.this.etqh();
				}
			});

		}

		@Override
		public int etq() {
			return iIF.etq();
		}

		@Override
		public Error err() {
			if (!hay_err) {
				err = iIF.err();
				hay_err = true;
			}
			return err;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = iIF.cod();
				hay_cod = true;
			}
			return cod;
		}
	}
	// ---------------------------------------------Inst----------------------------------------------------
	public class InstR3 extends Inst {
		private Error err;
		private List<Instruccion> cod;
		private boolean hay_err;
		private boolean hay_cod;
		private Inst iDO;

		public InstR3(Inst iDO) {
			this.iDO = iDO;
			iDO.registraCtx(new InstCtx() {
				public TS tsh() {
					return InstR3.this.tsh();
				}
				public int etqh() {
					return InstR3.this.etqh();
				}
			});
		}

		@Override
		public int etq() {
			return iDO.etq();
		}

		@Override
		public Error err() {
			if (!hay_err) {
				err = iDO.err();
				hay_err = true;
			}
			return err;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = iDO.cod();
				hay_cod = true;
			}
			return cod;
		}
	}

	public class IAsigR1 extends Inst {

		private Token iden;
		private Exp0 exp0;

		public IAsigR1(Token iden, Exp0 exp0) {
			this.iden = iden;
			this.exp0 = exp0;
			exp0.registraCtx(new Exp0Ctx() {
				public TS tsh() {
					return IAsigR1.this.tsh();
				}
				public int etqh() {
					return IAsigR1.this.etqh();
				}
			});
		}

		public int etq() {
			return (exp0.etq() + 1);
		}

		public Error err() {
			Error e;
			if (!(asignacionCorrecta(iden.leeLexema(), tsh(), exp0.tipo()))) {
				e = new Error("Asignacion incorrecta");
				return e;
			}
			return noError();
		}

		public List<Instruccion> cod() {
			return concat(
					exp0.cod(),
					Instruccion.nuevaIDesapilaDir(String.valueOf(dirDe(
							iden.leeLexema(), tsh()))));
		}
	}
	// --------------------------------------------IIfR1-----------------------------------------------------
	public class IIfR1 extends Inst {
		private Error err;
		private List<Instruccion> cod;
		private boolean hay_err;
		private boolean hay_cod;
		private Casos casos;

		public IIfR1(Casos casos) {
			this.casos = casos;
			casos.registraCtx(new CasosCtx() {
				public TS tsh() {
					return IIfR1.this.tsh();
				}
				public int etqh() {
					return IIfR1.this.etqh();
				}

				public int irh() {
					return IIfR1.this.casos.etq();
				}
			});
		}

		public int etq() {
			return casos.etq();
		}

		public Error err() {
			if (!hay_err) {
				err = casos.err();
				hay_err = true;
			}
			return err;
		}

		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = casos.cod();
				hay_cod = true;
			}
			return cod;
		}
	}
	// -----------------------------------------------IDoR1--------------------------------------------------
	public class IDoR1 extends Inst {
		private Error err;
		private List<Instruccion> cod;
		private boolean hay_err;
		private boolean hay_cod;
		private Casos casos;

		public IDoR1(Casos casos) {
			this.casos = casos;
			casos.registraCtx(new CasosCtx() {
				public TS tsh() {
					return IDoR1.this.tsh();
				}

				public int etqh() {
					return IDoR1.this.etqh();
				}

				public int irh() {
					return IDoR1.this.etqh(); 
				}
			});
		}

		public int etq() {
			return casos.etq();
		}

		public Error err() {
			if (!hay_err) {
				err = casos.err();
				hay_err = true;
			}
			return err;
		}

		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = casos.cod();
				hay_cod = true;
			}
			return cod;
		}
	}
	// --------------------------------------------CasosR1---------------------------------------------------
	public class CasosR1 extends Casos {
		private Error err;
		private List<Instruccion> cod;
		private boolean hay_err;
		private boolean hay_cod;
		private Casos casos1;
		private Caso caso;

		public CasosR1(Casos casos, Caso caso) {
			this.casos1 = casos;
			this.caso = caso;
			casos1.registraCtx(new CasosCtx() {
				public TS tsh() {
					return CasosR1.this.tsh();
				}

				public int etqh() {
					return CasosR1.this.etqh();
				}

				public int irh() {
					return CasosR1.this.irh();
				}
			});
			caso.registraCtx(new CasoCtx() {
				public TS tsh() {
					return CasosR1.this.tsh();
				}

				public int etqh() {
					return CasosR1.this.casos1.etq();
				}

				public int irh() {
					return CasosR1.this.irh();
				}
			});
		}

		@Override
		public int etq() {
			return caso.etq();
		}

		@Override
		public Error err() {
			if (!hay_err) {
				err = joinErrors(casos1.err(), caso.err());
				hay_err = true;
			}
			return err;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = concat(casos1.cod(), caso.cod());
				hay_cod = true;
			}
			return cod;
		}
	}
	// --------------------------------------------CasosR2---------------------------------------------------
	public class CasosR2 extends Casos {
		private Error err;

		private List<Instruccion> cod;
		private boolean hay_err;
		private boolean hay_cod;
		private Caso caso;

		public CasosR2(Caso caso) {
			this.caso = caso;
			caso.registraCtx(new CasoCtx() {
				public TS tsh() {
					return CasosR2.this.tsh();
				}
				public int etqh() {
					return CasosR2.this.etqh();
				}

				public int irh() {
					return CasosR2.this.irh();
				}
			});
		}

		@Override
		public int etq() {
			return caso.etq();
		}

		@Override
		public Error err() {
			if (!hay_err) {
				err = caso.err();
				hay_err = true;
			}
			return err;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = caso.cod();
				hay_cod = true;
			}
			return cod;
		}
	}
	// ----------------------------------------------CasoR1--------------------------------------------------
	public class CasoR1 extends Caso {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp0 exp0;
		private Insts insts;

		public CasoR1(Exp0 exp0, Insts insts) {
			this.exp0 = exp0;
			this.insts = insts;
			exp0.registraCtx(new Exp0Ctx() {
				public TS tsh() {
					return CasoR1.this.tsh();
				}
				public int etqh() {
					return CasoR1.this.etqh();
				}

			});
			insts.registraCtx(new InstsCtx() {
				public TS tsh() {
					return CasoR1.this.tsh();
				}
				public int etqh() {
					return CasoR1.this.exp0.etq() + 1;
				}

			});
		}

		@Override
		public int etq() {
			return (insts.etq() + 1);
		}

		@Override
		public Error err() {
			boolean e = (exp0.tipo().compareTo(CatLexica.BOOLEAN) == 0);
			Error error;
			if (!e) {
				error = new Error("Error de tipos");
			} else
				error = noError();
			return joinErrors(insts.err(), error);
		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = concat(exp0.cod(),
						Instruccion.nuevaIIr_f(insts.etq() + 1));
				cod = concat(cod, insts.cod());
				cod = concat(cod, Instruccion.nuevaIIr_a(irh()));
				hay_cod = true;
			}
			return cod;
		}
	}
	// -----------------------------------------------Exp0R1------------------------------------------------
	public class Exp0R1 extends Exp0 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp1 exp1_0;
		private Exp1 exp1_1;
		private OpComparacion opc;

		public Exp0R1(Exp1 exp1_0, Exp1 exp1_1, OpComparacion opc) {
			this.exp1_0 = exp1_0;
			this.exp1_1 = exp1_1;
			this.opc = opc;

			exp1_0.registraCtx(new Exp1Ctx() {
				public TS tsh() {
					return Exp0R1.this.tsh();
				}
				public int etqh() {
					return Exp0R1.this.etqh();
				}

			});
			exp1_1.registraCtx(new Exp1Ctx() {
				public TS tsh() {
					return Exp0R1.this.tsh();
				}
				public int etqh() {
					return Exp0R1.this.exp1_0.etq();
				}

			});
		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = concat(exp1_0.cod(), concat(exp1_1.cod(), opc.cod()));
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {
			return exp1_1.etq() + 1;
		}

		@Override
		public CatLexica tipo() {
			return tipoOpBin(opc.op(), exp1_0.tipo(), exp1_1.tipo());
		}

	}
	// -------------------------------------------Exp0R2-----------------------------------------------------
	public class Exp0R2 extends Exp0 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp1 exp1;

		public Exp0R2(Exp1 exp1) {
			this.exp1 = exp1;

			exp1.registraCtx(new Exp1Ctx() {
				public TS tsh() {
					return Exp0R2.this.tsh();
				}
				public int etqh() {
					return Exp0R2.this.etqh();
				}

			});

		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = exp1.cod();
				hay_cod = true;
			}
			return cod;
		}
		@Override
		public int etq() {
			return exp1.etq();
		}

		@Override
		public CatLexica tipo() {
			return exp1.tipo();
		}

	}
	// --------------------------------------------Exp1R1--------------------------------------------------
	public class Exp1R1 extends Exp1 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp1 exp1;
		private Exp2 exp2;

		private OpAditivo opa;

		public Exp1R1(Exp1 exp1, Exp2 exp2, OpAditivo opa) {
			this.exp1 = exp1;
			this.exp2 = exp2;
			this.opa = opa;

			exp1.registraCtx(new Exp1Ctx() {
				public TS tsh() {
					return Exp1R1.this.tsh();
				}
				public int etqh() {
					return Exp1R1.this.etqh();
				}

			});
			exp2.registraCtx(new Exp2Ctx() {
				public TS tsh() {
					return Exp1R1.this.tsh();
				}
				public int etqh() {
					return Exp1R1.this.exp1.etq();
				}

			});
		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = concat(exp1.cod(), concat(exp2.cod(), opa.cod()));
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {
			return exp2.etq() + 1;
		}

		@Override
		public CatLexica tipo() {
			return tipoOpBin(opa.op(), exp1.tipo(), exp2.tipo());
		}

	}
	// ---------------------------------------------Exp1R2---------------------------------------------------
	public class Exp1R2 extends Exp1 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp2 exp2;

		public Exp1R2(Exp2 exp2) {
			this.exp2 = exp2;

			exp2.registraCtx(new Exp2Ctx() {
				public TS tsh() {
					return Exp1R2.this.tsh();
				}
				public int etqh() {
					return Exp1R2.this.etqh();
				}

			});

		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = exp2.cod();
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {

			return exp2.etq();
		}

		@Override
		public CatLexica tipo() {
			return exp2.tipo();
		}

	}

	// --------------------------------------------Exp2R1----------------------------------------------------
	public class Exp2R1 extends Exp2 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp2 exp2;
		private Exp3 exp3;

		private OpMultiplicativo opm;

		public Exp2R1(Exp2 exp2, Exp3 exp3, OpMultiplicativo opm) {
			this.exp2 = exp2;
			this.exp3 = exp3;
			this.opm = opm;

			exp2.registraCtx(new Exp2Ctx() {
				public TS tsh() {
					return Exp2R1.this.tsh();
				}

				public int etqh() {
					return Exp2R1.this.etqh();
				}

			});
			exp3.registraCtx(new Exp3Ctx() {
				public TS tsh() {
					return Exp2R1.this.tsh();
				}
				public int etqh() {
					return Exp2R1.this.exp2.etq();
				}

			});
		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = concat(exp2.cod(), concat(exp3.cod(), opm.cod()));
				hay_cod = true;
			}
			return cod;
		}
		@Override
		public int etq() {
			return exp3.etq() + 1;
		}

		@Override
		public CatLexica tipo() {
			return tipoOpBin(opm.op(), exp2.tipo(), exp3.tipo());
		}

	}
	// ----------------------------------------Exp2R2-------------------------------------------------------
	public class Exp2R2 extends Exp2 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp3 exp3;

		public Exp2R2(Exp3 exp3) {
			this.exp3 = exp3;

			exp3.registraCtx(new Exp3Ctx() {
				public TS tsh() {
					return Exp2R2.this.tsh();
				}
				public int etqh() {
					return Exp2R2.this.etqh();
				}

			});

		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = exp3.cod();
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {
			return exp3.etq();
		}

		@Override
		public CatLexica tipo() {
			return exp3.tipo();
		}

	}
	// --------------------------------------------Exp3R1----------------------------------------------------
	public class Exp3R1 extends Exp3 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp3 exp3;
		private OpUnario opu;

		public Exp3R1(Exp3 exp3, OpUnario opu) {
			this.exp3 = exp3;
			this.opu = opu;
			exp3.registraCtx(new Exp3Ctx() {
				public TS tsh() {
					return Exp3R1.this.tsh();
				}

				public int etqh() {
					return Exp3R1.this.etqh();
				}

			});

		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = concat(exp3.cod(), opu.cod());
				hay_cod = true;
			}
			return cod;
		}


		@Override
		public int etq() {
			return (exp3.etq() + 1);
		}

		@Override
		public CatLexica tipo() {
			return tipoOpUnario(opu.op(), exp3.tipo());
		}

	}
	// ---------------------------------------------Exp3R2--------------------------------------------------
	public class Exp3R2 extends Exp3 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp4 exp4;

		public Exp3R2(Exp4 exp4) {
			this.exp4 = exp4;

			exp4.registraCtx(new Exp4Ctx() {
				public TS tsh() {
					return Exp3R2.this.tsh();
				}

				public int etqh() {
					return Exp3R2.this.etqh();
				}

			});

		}

		@Override
		public List<Instruccion> cod() {

			if (!hay_cod) {
				cod = exp4.cod();
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {
			return exp4.etq();
		}

		@Override
		public CatLexica tipo() {
			return exp4.tipo();
		}

	}
	// ------------------------------------------Exp4R1-----------------------------------------------------
	public class Exp4R1 extends Exp4 {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public Exp4R1() {

		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();

				cod.add(Instruccion.nuevaIApilaTrue());
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {
			return this.etqh() + 1;
		}

		@Override
		public CatLexica tipo() {
			return CatLexica.BOOLEAN;
		}

	}
	// --------------------------------------------Exp4R2----------------------------------------------------
	public class Exp4R2 extends Exp4 {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public Exp4R2() {
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIApilaFalse());
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {

			return this.etqh() + 1;

		}

		@Override
		public CatLexica tipo() {
			return CatLexica.BOOLEAN;
		}

	}
	// ------------------------------------------Exp4R3-----------------------------------------------------
	public class Exp4R3 extends Exp4 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Token num;

		public Exp4R3(Token num) {
			this.num = num;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIApilaInt(num.leeLexema()));
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {

			return this.etqh() + 1;
		}

		@Override
		public CatLexica tipo() {
			return CatLexica.INT;
		}

	}
	// ----------------------------------------------Ep4R4-------------------------------------------------
	public class Exp4R4 extends Exp4 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Token iden;

		public Exp4R4(Token iden) {
			this.iden = iden;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIApilaDir(String.valueOf(dirDe(
						iden.leeLexema(), tsh()))));
				hay_cod = true;
			}
			return cod;
		}

		@Override
		public int etq() {
			return this.etqh() + 1;
		}

		@Override
		public CatLexica tipo() {
			return tipoDe(iden.leeLexema(), tsh());
		}

	}
	// --------------------------------------------Exp4R5---------------------------------------------------
	public class Exp4R5 extends Exp4 {

		private List<Instruccion> cod;
		private boolean hay_cod;
		private Exp0 exp0;

		public Exp4R5(Exp0 exp0) {
			this.exp0 = exp0;

			exp0.registraCtx(new Exp0Ctx() {
				public TS tsh() {
					return Exp4R5.this.tsh();
				}

				@Override
				public int etqh() {
					return Exp4R5.this.etqh();
				}
			});
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = exp0.cod();
			}
			return cod;
		}

		@Override
		public int etq() {

			return exp0.etq();
		}

		@Override
		public CatLexica tipo() {
			return exp0.tipo();
		}

	}
	// -----------------------------------------OpCompR1---------------------------------------------------
	public class OpCompR1 implements OpComparacion {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpCompR1() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.EQ;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIEq());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpCompR2-------------------------------------------------
	public class OpCompR2 implements OpComparacion {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpCompR2() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.NEQ;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaINeq());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpCompR3---------------------------------------------------
	public class OpCompR3 implements OpComparacion {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpCompR3() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.GT;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIGt());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpCompR4---------------------------------------------------
	public class OpCompR4 implements OpComparacion {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpCompR4() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.GE;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIGe());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpCompR5---------------------------------------------------
	public class OpCompR5 implements OpComparacion {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpCompR5() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.LT;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaILt());
				hay_cod = true;
			}
			return cod;
		}
	}

	// -------------------------------------------OpCompR6---------------------------------------------------
	public class OpCompR6 implements OpComparacion {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpCompR6() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.LE;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaILe());
				hay_cod = true;
			}
			return cod;
		}
	}

	// -------------------------------------------OpAdivR1---------------------------------------------------
	public class OpAdivR1 implements OpAditivo {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpAdivR1() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.MAS;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaISuma());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpAdivR2---------------------------------------------------
	public class OpAdivR2 implements OpAditivo {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpAdivR2() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.MENOS;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIResta());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpAdivR3---------------------------------------------------
	public class OpAdivR3 implements OpAditivo {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpAdivR3() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.OR;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIOr());
				hay_cod = true;
			}
			return cod;
		}
	}

	// -------------------------------------------OpMultR1---------------------------------------------------
	public class OpMultR1 implements OpMultiplicativo {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpMultR1() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.MULT;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIMul());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpMultR2---------------------------------------------------
	public class OpMultR2 implements OpMultiplicativo {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpMultR2() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.DIV;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIDiv());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpMultR3---------------------------------------------------
	public class OpMultR3 implements OpMultiplicativo {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpMultR3() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.AND;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIAnd());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpUnarioR1---------------------------------------------------
	public class OpUnarioR1 implements OpUnario {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpUnarioR1() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.MENOS;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaIMenos());
				hay_cod = true;
			}
			return cod;
		}
	}
	// -------------------------------------------OpUnarioR2---------------------------------------------------
	public class OpUnarioR2 implements OpUnario {

		private List<Instruccion> cod;
		private boolean hay_cod;

		public OpUnarioR2() {
			this.hay_cod = false;
		}

		@Override
		public CatLexica op() {
			return CatLexica.NOT;
		}

		@Override
		public List<Instruccion> cod() {
			if (!hay_cod) {
				cod = new ArrayList<Instruccion>();
				cod.add(Instruccion.nuevaINot());
				hay_cod = true;
			}
			return cod;
		}
	}
	
	// -----------------------------------------------------------------------------------------------------

	public TS aniadeSimb(TS ts, String iden, CatLexica tipo, int dir) {
		ArrayList<Object> a = new ArrayList<Object>();
		a.add(tipo);
		a.add(String.valueOf(dir));
		return ts.aniade(iden, a);
	}

	public boolean estaEn(String cte, TS ts) {
		return ts.estaEn(cte);
	}

	public CatLexica tipoDe(String cte, TS ts) {
		return (CatLexica) ts.valDe(cte).get(0); 
	}

	public int dirDe(String lex, TS ts) {
		return Integer.valueOf((String) ts.valDe(lex).get(1));
	}

	public TS creaTS() {
		return new TS();
	}

	public List<Instruccion> concat(List<Instruccion> c1, List<Instruccion> c2) {
		List<Instruccion> result = new LinkedList<Instruccion>(c1);
		result.addAll(c2);
		return result;
	}

	public List<Instruccion> concat(List<Instruccion> c1, Instruccion i) {
		List<Instruccion> result = new LinkedList<Instruccion>(c1);
		result.add(i);
		return result;
	}

	public CatLexica tipoOpBin(CatLexica op, CatLexica tipo2, CatLexica tipo3) {
		if (tipo2.equals(tipo3)) {
			if (op.equals(CatLexica.GT) || op.equals(CatLexica.EQ)
					|| op.equals(CatLexica.NEQ) || op.equals(CatLexica.GE)
					|| op.equals(CatLexica.LT) || op.equals(CatLexica.LE))
				return CatLexica.BOOLEAN;
			else if (op.equals(CatLexica.MAS) || op.equals(CatLexica.MENOS)
					|| op.equals(CatLexica.MULT) || op.equals(CatLexica.DIV))
				return CatLexica.INT;

		}
		return CatLexica.ERROR;
	}

	public CatLexica tipoOpUnario(CatLexica op, CatLexica tipo) {
		if (op.equals(CatLexica.MENOS) && tipo.equals(CatLexica.INT))
			return tipo;
		else if (op.equals(CatLexica.NOT) && tipo.equals(CatLexica.BOOLEAN))
			return tipo;
		else
			return CatLexica.ERROR;
	}

	public Instruccion suma() {
		return Instruccion.nuevaISuma();
	}

	public Instruccion mul() {
		return Instruccion.nuevaIMul();
	}

	public Instruccion apila(String arg) {
		return Instruccion.nuevaIApila(arg);
	}

	public List<Instruccion> single(Instruccion i) {
		List<Instruccion> is = new LinkedList<Instruccion>();
		is.add(i);
		return is;
	}

	public Error noError() {
		return new Error();
	}

	public Error joinErrors(Error e1, Error e2) {
		return new Error(e1, e2);
	}

	public Error errorSi(boolean condicion, int fila, int col, String msg) {
		if (condicion) {
			return new Error("(" + fila + "," + col + ") ERROR CONTEXTUAL: "
					+ msg);
		} else
			return new Error();
	}

	private boolean existeSimb(TS ts2, String iden) {
		return ts2.estaEn(iden);
	}

	private boolean asignacionCorrecta(String leeLexema, TS tsh, CatLexica tipo) {
		return (tipoDe(leeLexema, tsh).compareTo(tipo) == 0);
	}

}
